﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace PassGenCS
{
    class Program
    {
        private int passwordLength = 16;

        /// <summary>
        ///  AES 加密
        /// </summary>
        /// <param name="str">明文（待加密）</param>
        /// <param name="key">密文</param>
        /// <returns></returns>
        private static string AesEncrypt(string str, string key)
        {
            if (string.IsNullOrEmpty(str)) return null;
            Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);

            RijndaelManaged rm = new RijndaelManaged
            {
                Key = Encoding.UTF8.GetBytes(key),
                Mode = CipherMode.ECB,
                Padding = PaddingMode.PKCS7
            };

            ICryptoTransform cTransform = rm.CreateEncryptor();
            Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        /// <summary>
        ///  AES 解密
        /// </summary>
        /// <param name="str">明文（待解密）</param>
        /// <param name="key">密文</param>
        /// <returns></returns>
        private static string AesDecrypt(string str, string key)
        {
            if (string.IsNullOrEmpty(str)) return null;
            Byte[] toEncryptArray = Convert.FromBase64String(str);

            RijndaelManaged rm = new RijndaelManaged
            {
                Key = Encoding.UTF8.GetBytes(key),
                Mode = CipherMode.ECB,
                Padding = PaddingMode.PKCS7
            };

            ICryptoTransform cTransform = rm.CreateDecryptor();
            Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

            return Encoding.UTF8.GetString(resultArray);
        }

        private static string ByteToBinString(byte b)
        {
            string s = Convert.ToString(b, 2);
            for (; s.Length < 8; s = "0" + s) ;
            return s;
        }

        private static byte ByteAnd(byte a, byte b)
        {
            char[] ac = ByteToBinString(a).ToCharArray();
            char[] bc = ByteToBinString(b).ToCharArray();
            int r = 0;
            for (int i = 0; i < 8; ++i)
            {
                if (ac[i] == '1' && bc[i] == '1')
                {
                    r += (int)Math.Pow(2, 7 - i);
                }
            }
            //Console.WriteLine("Debug a={0}", ByteToBinString(a));//
            //Console.WriteLine("Debug b={0}", ByteToBinString(b));//
            //Console.WriteLine("Debug r={0}", r);//
            //Console.WriteLine("Debug r(2)={0}", ByteToBinString((byte)r));//
            return (byte)r;
        }

        private static byte ByteNor(byte a, byte b)
        {
            char[] ac = ByteToBinString(a).ToCharArray();
            char[] bc = ByteToBinString(b).ToCharArray();
            int r = 0;
            for (int i = 0; i < 8; ++i)
            {
                if (ac[i] != bc[i])
                {
                    r += (int)Math.Pow(2, 7 - i);
                }
            }
            //Console.WriteLine("Debug a={0}", ByteToBinString(a));//
            //Console.WriteLine("Debug b={0}", ByteToBinString(b));//
            //Console.WriteLine("Debug r={0}", r);//
            //Console.WriteLine("Debug r(2)={0}", ByteToBinString((byte)r));//
            return (byte)r;
        }

        private static string MyBytesToString(byte[] bytes)
        {
            string list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-";
            char[] lc = list.ToCharArray();
            string res = "";
            for(int i = 0; i < bytes.Length; ++i)
            {
                res += lc[bytes[i] % lc.Length];
            }
            return res;
        }

        private static string GenPass(string basePassword, string additionalPassword, int length)
        {
            byte[] bytes = new byte[length];
            for (; additionalPassword.Length < 16; additionalPassword += "0") ;
            //Console.WriteLine("Debug additionalPassword={0}", additionalPassword);//
            string password = AesEncrypt(basePassword, additionalPassword);
            byte[] passwordBytes = System.Text.Encoding.Default.GetBytes(password);
            //Console.WriteLine("Debug password={0}", password);//
            #region Debug output
            /*
            for (int i = 0; i < passwordBytes.Length; ++i)
            {
                Console.Write("{0}\t", ByteToBinString(passwordBytes[i]));//
                if (i % 4 == 3)
                {
                    Console.Write("\n");
                }
            }
            */
            #endregion
            for (int i = 0; i < length; ++i)
            {
                bytes[i] = passwordBytes[i];
            }
            for (int i = 1; i < Math.Ceiling((double)passwordBytes.Length / length); ++i)
            {
                for (int k = 0; k < length && length * i + k < passwordBytes.Length; ++k)
                {
                    bytes[k] = ByteNor(bytes[k], passwordBytes[length * i + k]);
                }
            }

            return MyBytesToString(bytes);
        }

        private static string GenUUID()
        {
            return System.Guid.NewGuid().ToString("N");
        }

        private static void ShowAbout()
        {
            Console.Write("\n\n这是一个密码生成软件，可以让您在固定的密码基础上添加一些简单的随机性来生成完全不同的复杂密码。\n" +
                "在您输入的条件相同的情况下，可以生成与上次完全相同的密码，所以您不必担心忘记复杂的密码。\n" +
                "原理：使用您的附加密码对基础密码进行AES加密后按密码长度分组进行异或运算，最后从特定的密码字符中选择对应的字符进行组合。\n\n" +
                "推荐设定：\n基础密码使用容易记忆的密码，如自己的姓名首字母" +
                "附加密码使用您账号所属网站的缩写，如QQ密码设置QQ，微信密码设置WX等便于记忆" +
                "密码长度推荐16位，这样随机性较强，而且长密码可以大大提高密码强度。\n\n");
        }

        static void Main(string[] args)
        {
            if (args.Length > 0)
            {
                Console.WriteLine(GenPass(args[0], args[1], Int32.Parse(args[2])));
                Console.ReadKey();
                return;
            }
            ShowAbout();
            string basePassword, additionalPassword;
            int length;
            Console.WriteLine("请输入基础密码，如不输入则会随机生成，这需要您记住生成的最终密码，因为将无法再次生成同样的密码。");
            basePassword=Console.ReadLine();
            if (String.IsNullOrEmpty(basePassword))
            {
                basePassword = GenUUID();
            }
            Console.WriteLine("请输入附加密码，这部分密码决定了您最终密码的变化，您依然可以选择不输入来随机生成，但同样请您牢记您的最终密码。");
            additionalPassword = Console.ReadLine();
            if (String.IsNullOrEmpty(additionalPassword))
            {
                additionalPassword = GenUUID();
            }
            Console.WriteLine("请输入您需要生成的密码长度，最长32位，其中16位密码随机性最强，默认16位。");
            try
            {
                length = Int32.Parse(Console.ReadLine());
                if (length < 1 || length > 32)
                {
                    length = 16;
                }
            }
            catch(Exception e)
            {
                length = 16;
            }
            
            Console.Write("基础密码：{0}\n附加密码：{1}\n密码长度：{2}\n最终密码：", basePassword, additionalPassword, length);
            Console.WriteLine(GenPass(basePassword, additionalPassword, length));
            Console.Write("\n按任意键结束程序\n");
            Console.ReadKey();
        }
    }
}
